//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
// Target Devices:
// Tool versions:
//
// Create Date:    2011-08-18 15:28
// Project Name:
// Description:
//      1.write data to BRAM
//      2.write data to ddr
//
// Dependencies:
//
// Revision: 1.0
// Revision 0.01 - File Created
//
//
//////////////////////////////////////////////////////////////////////////////////
`timescale 1ns/1ns
module cmos_intf
#(
    parameter H_SIZE = 320,
    parameter V_SIZE = 240
)
(
    input               clk,
    input               rst,

    // config & status
    input               cmos_id,        // 0 means cmos x, 1 means cmos y
    input [15:0]        offset_h,
    input [15:0]        offset_v,
    input [29:0]        ddr_buf0_addr,
    input [29:0]        ddr_buf1_addr,
    output reg [1:0]    ddr_buf_done,    // 2'b00,busy; 2'b01,buffer0 done,2'b10,bufer1 done; 2'b11 no use
    output reg[15:0]    status,
    // CAM
    output              cam_mclk,
    input [7:0]         cam_d,
    input               cam_vsync,
    input               cam_hsync,
    input               cam_href,
    input               cam_pclk,

    //cmos ddr write
    output              cmos_cmd_clk,
    output              cmos_cmd_en,
    output [2:0]        cmos_cmd_instr,
    output [5:0]        cmos_cmd_bl,
    output [29:0]       cmos_cmd_byte_addr,
    input               cmos_cmd_full,

    output              cmos_wr_clk,
    output              cmos_wr_en,
    output [3:0]        cmos_wr_mask,
    output [31:0]       cmos_wr_data,
    input               cmos_wr_full,

     //Frame buffer write port
    output              buffer_clk,
    output reg          buffer_wr,
    output reg[16:0]    buffer_addr,
    output reg[7:0]     buffer_d
);

/********************************************************\
Parameter
\********************************************************/
localparam U_DLY        = 1;
localparam DDR_WR       = 3'b000;
localparam V_LIMIT      = V_SIZE/2;
localparam CMOS_Y_OFFSET= 30'h9600;

/********************************************************\
Signals
\********************************************************/
// pclk signal
reg [15:0]          h_cnt;
reg [15:0]          v_cnt;
reg [15:0]          offset_v_reg;
reg [15:0]          offset_h_reg;
reg [15:0]          v_max_reg;
reg [15:0]          bram_v_max_reg;
reg [15:0]          h_max_reg;
reg                 cam_hsync_reg0;
reg                 cam_hsync_reg1;
wire                cam_hsync_falling;
reg                 cam_vsync_reg0;
reg                 cam_vsync_reg1;
wire                cam_vsync_falling;
wire                v_valid;
wire                bram_v_valid;
wire                h_valid;

// fifo port
reg                 fifo_wr_en;
wire                fifo_alfull;
wire                fifo_full;
reg [7:0]           fifo_wrdata;

reg                 fifo_rd_en;
reg                 fifo_rd_en_dly1;
reg                 fifo_rd_en_dly2;
wire                fifo_alempty;
wire                fifo_empty;
wire [31:0]         fifo_rddata;

// clk signal
reg                 vsync_falling;
wire                ddr_fifo_full;
reg [29:0]          ddr_addr;
reg                 ddr_buf_sel; // ddr frame buffer select

reg [7:0]           cam_d_reg0;
reg [7:0]           cam_d_reg1;
reg                 v_valid_reg;
reg                 v_valid_reg1;
wire                v_valid_falling;

/********************************************************\
main code
\********************************************************/
assign cmos_cmd_clk     = clk;
assign cmos_cmd_en      = fifo_rd_en_dly2;
assign cmos_cmd_instr   = DDR_WR;
assign cmos_cmd_bl      = 'h0;        // burst lenght is 1;
assign cmos_cmd_byte_addr= ddr_addr;

assign cmos_wr_en       = fifo_rd_en_dly1;
assign cmos_wr_data     = {fifo_rddata[7:0],fifo_rddata[15:8],fifo_rddata[23:16],fifo_rddata[31:24]};
assign cmos_wr_mask     = 'h0;        // no mask;
assign cmos_wr_clk      = clk;
assign ddr_fifo_full    = cmos_cmd_full | cmos_wr_full;

assign cam_hsync_falling= cam_hsync_reg1 & (~cam_hsync_reg0);
assign cam_vsync_falling= cam_vsync_reg1 & (~cam_vsync_reg0);
assign v_valid          = (v_cnt >= offset_v_reg) & (v_cnt < v_max_reg);
assign bram_v_valid     = (v_cnt >= offset_v_reg) & (v_cnt < bram_v_max_reg);
assign h_valid          = (h_cnt >= offset_h_reg) & (h_cnt < h_max_reg);

assign v_valid_falling  = v_valid_reg1 & (~v_valid_reg);

assign buffer_clk       = cam_pclk;

/*********************** PCLK domain *************************/

always@(posedge cam_pclk)
begin
    cam_hsync_reg0  <= #1 cam_hsync;
    cam_hsync_reg1  <= #1 cam_hsync_reg0;
end

always@(posedge cam_pclk)
begin
    cam_vsync_reg0  <= #1 cam_vsync;
    cam_vsync_reg1  <= #1 cam_vsync_reg0;
end

always@(posedge cam_pclk)
begin
    if(cam_vsync==1'b1)
    begin
        h_cnt   <= 'h0;
    end
    else if(cam_href)
    begin
        h_cnt   <= #1 h_cnt + 1'b1;
    end
    else
    begin
        h_cnt   <= #1 'h0;
    end
end

always@(posedge cam_pclk)
begin
    if(cam_vsync==1'b1)
    begin
        v_cnt   <= 'h0;
    end
    else if(cam_hsync_falling)
    begin
        v_cnt   <= #1 v_cnt + 1'b1;
    end
end

always@(posedge cam_pclk)
begin
    if(cam_vsync==1'b1)
    begin
        offset_v_reg    <= 'h0;
        offset_h_reg    <= 'h0;
        v_max_reg       <= 'h0;
        bram_v_max_reg  <= 'h0;
        h_max_reg       <= 'h0;
    end
    else if(cam_vsync_falling)
    begin
        offset_v_reg    <= #1 offset_v;
        offset_h_reg    <= #1 offset_h;
        v_max_reg       <= #1 offset_v + V_LIMIT;
        bram_v_max_reg  <= #1 offset_v + V_SIZE;
        h_max_reg       <= #1 offset_h + H_SIZE;
    end
end

always@(posedge cam_pclk)
begin
    fifo_wr_en  <= #1 v_valid & h_valid & cam_href;
    fifo_wrdata <= #1 cam_d;
end

/*********************** BRAM write *************************/

always@(posedge cam_pclk)
begin
    if(bram_v_valid & h_valid & cam_href)
    begin
        buffer_wr <= #1 1'b1;
    end
    else
    begin
        buffer_wr <= #1 1'b0;
    end
end

always@(posedge cam_pclk)
begin
    if(cam_vsync==1'b1)
    begin
        buffer_d <= 'h0;
    end
    else if(bram_v_valid & h_valid & cam_href)
    begin
        buffer_d <= #1 cam_d;
    end
end

always@(posedge cam_pclk)
begin
    if(cam_vsync==1'b1)
    begin
        buffer_addr <= #1 'h0;
    end
    else if(buffer_wr)
    begin
        buffer_addr <= #1 buffer_addr + 1'b1;
    end
end

/*********************** ddr write *************************/

always@(posedge clk ,posedge rst)
begin
    if(rst==1'b1)
    begin
        vsync_falling       <= 1'b0; 
        v_valid_reg <= 1'b0;
        v_valid_reg1<= 1'b0;
    end
    else
    begin
        vsync_falling       <= #1 cam_vsync_falling;
        v_valid_reg         <= #1 v_valid;
        v_valid_reg1        <= #1 v_valid_reg;
    end
end
always@(posedge clk ,posedge rst)
begin
    if(rst==1'b1)
    begin
        ddr_buf_sel  <= 1'b0;
    end
    else if(v_valid_falling)
    begin
        ddr_buf_sel    <= #1 ~ddr_buf_sel;
    end
end

always@(posedge clk ,posedge rst)
begin
    if(rst==1'b1)
    begin
        ddr_buf_done    <= 2'b00;
    end
    else if(v_valid_falling)
    begin
        ddr_buf_done    <= #1 (ddr_buf_sel==1'b0)?2'b01:2'b10;
    end
end

always@(posedge clk ,posedge rst)
begin
    if(rst==1'b1)
    begin
        ddr_addr    <= 'h0;
    end
    else if(vsync_falling)
    begin
        case({ddr_buf_sel,cmos_id})
            2'b00:ddr_addr  <= #1 ddr_buf0_addr;
            2'b01:ddr_addr  <= #1 ddr_buf0_addr + CMOS_Y_OFFSET;
            2'b10:ddr_addr  <= #1 ddr_buf1_addr;
            2'b11:ddr_addr  <= #1 ddr_buf1_addr + CMOS_Y_OFFSET;
        endcase
    end
    else if(cmos_cmd_en)
    begin
        ddr_addr    <= #1 ddr_addr + 'd4;
    end
end

always@(posedge clk ,posedge rst)
begin
    if(rst==1'b1)
    begin
        fifo_rd_en  <= 1'b0;
    end
    else if(~fifo_rd_en & (~ddr_fifo_full))
    begin
        fifo_rd_en    <= #1 ~fifo_empty;
    end
    else
    begin
        fifo_rd_en    <= #1 1'b0;
    end
end

always@(posedge clk ,posedge rst)
begin
    if(rst==1'b1)
    begin
        fifo_rd_en_dly1 <= 1'b0;
        fifo_rd_en_dly2 <= 1'b0;
    end
    else
    begin
        fifo_rd_en_dly1 <= #1 fifo_rd_en;
        fifo_rd_en_dly2 <= #1 fifo_rd_en_dly1;
    end
end

// instance
DCFIFO_2048_8I32O_BRAM cmos_buff(
  .rst          (rst),
  .wr_clk       (cam_pclk),
  .rd_clk       (clk),
  .din          (fifo_wrdata),
  .wr_en        (fifo_wr_en),
  .rd_en        (fifo_rd_en),
  .dout         (fifo_rddata),
  .full         (fifo_full),
  .almost_full  (fifo_alfull),
  .empty        (fifo_empty),
  .almost_empty (fifo_alempty)
);
endmodule
